
import java.nio.ByteBuffer;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.ARBVertexBufferObject;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.util.glu.GLU;

public class x {

    public static void main(String[] args) {
        //init display
        Display.setTitle("vbo test");
        try {
            DisplayMode mode = new DisplayMode(800, 600);
            Display.setDisplayMode(mode);
            Display.setFullscreen(true);
            Display.create();
        } catch (LWJGLException e) {
            e.printStackTrace();
            System.exit(0);
        }

        //init OpenGL
        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);//_MINUS_SRC_ALPHA);

        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        GL11.glFrustum(-1, 1, -1, 1, 1, 1000);

        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glLoadIdentity();
        GLU.gluLookAt(0, 10, 0, 0, 0, 0, 0, 0, 1);

        if (!GLContext.getCapabilities().GL_ARB_vertex_buffer_object) {
            throw new RuntimeException("VBOs unsupported");
        }

        //Create two VBO ids, one for vertice data and one for index data
        int dataVboId = ARBVertexBufferObject.glGenBuffersARB();
        int indexVboId = ARBVertexBufferObject.glGenBuffersARB();

        //The vertex buffer will contain interleaved data :
        //	-3d vertex (in doubles)
        //	-3d normal (in doubles)
        //	-RGBA color (in floats)
        //Get the size of primitive types in bytes
        int intSize = Integer.SIZE / 8;
        int floatSize = Float.SIZE / 8;
        int doubleSize = Double.SIZE / 8;
        //Get the size of a 3d vector (in doubles) and RGBA color (in floats)
        int verticeSize = 3 * doubleSize;
        int normalSize = 3 * doubleSize;
        int colorSize = 4 * floatSize;

        //Get the size of an element
        int elementSize = verticeSize + normalSize + colorSize;

        //Allocate the vertex buffer for three elements (to test displaying a triangle)
        ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, dataVboId);
        ARBVertexBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB,
                elementSize * 3, ARBVertexBufferObject.GL_STATIC_DRAW_ARB);

        //Map the vertex buffer to a ByteBuffer
        ByteBuffer dataBuffer = ARBVertexBufferObject.glMapBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB,
                ARBVertexBufferObject.GL_WRITE_ONLY_ARB, elementSize * 3, null);

        //write vertex {5,0,5}, normal {0,1,0}, color {0,0,1,1}
        dataBuffer.putDouble(5);
        dataBuffer.putDouble(0);
        dataBuffer.putDouble(5);
        dataBuffer.putDouble(0);
        dataBuffer.putDouble(1);
        dataBuffer.putDouble(0);
        dataBuffer.putFloat(0).putFloat(0).putFloat(1).putFloat(1);

        //write vertex {-5,0,5}, normal {0,1,0}, color {0,0,1,1}
        dataBuffer.putDouble(-5);
        dataBuffer.putDouble(0);
        dataBuffer.putDouble(5);
        dataBuffer.putDouble(0);
        dataBuffer.putDouble(1);
        dataBuffer.putDouble(0);
        dataBuffer.putFloat(0).putFloat(0).putFloat(1).putFloat(1);

        //write vertex {-5,0,-5}, normal {0,1,0}, color {0,0,1,1}
        dataBuffer.putDouble(-5);
        dataBuffer.putDouble(0);
        dataBuffer.putDouble(-5);
        dataBuffer.putDouble(0);
        dataBuffer.putDouble(1);
        dataBuffer.putDouble(0);
        dataBuffer.putFloat(0).putFloat(0).putFloat(1).putFloat(1);

        //flip buffer for reading
        dataBuffer.flip();

        //unmap and unbind
        ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB);
        ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);

        //Allocate the index buffer for three elements (they will point to the three elements above)
        ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexVboId);
        ARBVertexBufferObject.glBufferDataARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB,
                intSize * 3, ARBVertexBufferObject.GL_STATIC_DRAW_ARB);

        //Map the index buffer to a ByteBuffer
        ByteBuffer indexBuffer = ARBVertexBufferObject.glMapBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB,
                ARBVertexBufferObject.GL_WRITE_ONLY_ARB, intSize * 3, null);

        //Write the indexes and flip
        indexBuffer.putInt(0).putInt(1).putInt(2);
        indexBuffer.flip();

        //unmap and unbind
        ARBVertexBufferObject.glUnmapBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB);
        ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);

        boolean useVbos = true;
        while (!Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
            GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

            if (useVbos) {
                //enable the arrays
                GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
                GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
                GL11.glEnableClientState(GL11.GL_COLOR_ARRAY);

                //bind the vertex buffer and the index buffer
                ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, dataVboId);
                ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexVboId);

                //the vertex pointer has an offset of 0 and a stride of elementSize
                GL11.glVertexPointer(3, GL11.GL_DOUBLE, elementSize, 0);
                //the normal pointer has an offset of verticeSize and a stride of elementSize
                GL11.glNormalPointer(GL11.GL_DOUBLE, elementSize, verticeSize);
                //the color pointer has an offset of (verticeSize+normalSize) and a stride of elementSize
                GL11.glColorPointer(4, GL11.GL_FLOAT, elementSize, verticeSize + normalSize);

                //Draw the triangle with three indexes (with 0 offset in the index buffer)
                GL11.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_INT, 0);

                //unbind the buffers
                ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);
                ARBVertexBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);

                //disable the arrays
                GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
                GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY);
                GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);
            } else {
                GL11.glColor4f(0, 0, 1, 1);
                GL11.glBegin(GL11.GL_TRIANGLES);
                GL11.glNormal3d(0, 1, 0);
                GL11.glVertex3d(5, 0, 5);
                GL11.glVertex3d(-5, 0, 5);
                GL11.glVertex3d(-5, 0, -5);
                GL11.glEnd();
            }

            Display.update();
        }
    }
}